import * as XLSX from 'xlsx';

declare var saveAs;

/**
 * xlsx工具类
 */
export class XlslTool {
  wb: XLSX.WorkBook;
  bookType: XLSX.BookType = 'xlsx';
  type: 'base64' | 'binary' | 'buffer' | 'file' = 'binary';
  sheetIndex = 0;

  constructor() {
    this.wb = XLSX.utils.book_new();
  }

  /**
   * 设置二维数组数据
   * 
   * @param {Array<Array<any>>} data 
   * @param {XLSX.AOA2SheetOpts} [opt] 
   * @param {string} [sheetName] 
   * @returns {XlslTool} 
   * @memberof XlslTool
   */
  setAoA(data: Array<Array<any>>, opt?: XLSX.AOA2SheetOpts, sheetName?: string): XlslTool {
    sheetName = sheetName || this.getSheetName();
    this.appendWs(XLSX.utils.aoa_to_sheet(data), sheetName);
    return this;
  }

  /**
   * 设置json数据
   * 
   * @param {any[]} data 
   * @param {XLSX.JSON2SheetOpts} [opt] 
   * @param {string} [sheetName] 
   * @returns {XlslTool} 
   * @memberof XlslTool
   */
  setJson(data: any[], opt?: XLSX.JSON2SheetOpts, sheetName?: string): XlslTool {
    sheetName = sheetName || this.getSheetName();
    this.appendWs(XLSX.utils.json_to_sheet(data, opt), sheetName);
    return this;
  }

  /**
   * 设置table标签元素
   * 
   * @param {*} table 
   * @param {XLSX.Table2SheetOpts} [opt] 
   * @param {string} [sheetName] 
   * @returns {XlslTool} 
   * @memberof XlslTool
   */
  setTable(table: any, opt?: XLSX.Table2SheetOpts, sheetName?: string): XlslTool {
    sheetName = sheetName || this.getSheetName();
    this.appendWs(XLSX.utils.table_to_sheet(table, opt), sheetName);
    return this;
  }

  /**
   * 读取excel文件
   * 
   * @param {DataTransfer} target 目标文件
   * @param {XLSX.Sheet2JSONOpts} [opt] 读取配置选项
   * @param {number} [index=0] 读取sheet索引
   * @returns {*} 
   * @memberof XlslTool
   */
  public static read(target: DataTransfer, opt?: XLSX.Sheet2JSONOpts, index: number = 0): any {
    let data: any;
    this.reader(target, (wb: XLSX.WorkBook) => {
      let wsname = wb.SheetNames[index];
      data = XLSX.utils.sheet_to_json(wb.Sheets[wsname], opt);
    });
    return data;
  }

  appendWs(ws: XLSX.WorkSheet, sheetName?: string): XlslTool {
    sheetName = sheetName || this.getSheetName();
    XLSX.utils.book_append_sheet(this.wb, ws, sheetName);
    return this;
  }

  /**
   * 保存文件
   * 
   * @param {string} [fileName='download'] 文件名称（不包含后缀） 
   * @memberof XlslTool
   */
  save(fileName: string = 'download') {
    this._validSaveAs();
    saveAs(this._render(), `${fileName}.${this.bookType}`);
  }

  private getSheetName() {
    return `Sheet${++this.sheetIndex}`;
  }

  private static reader(target: DataTransfer, callback: (ws: XLSX.WorkBook) => void) {
    if (target.files.length !== 1) throw new Error('XlslTool: Cannot use multiple files');
    const reader: FileReader = new FileReader();
    reader.onload = (e: any) => {
      const bstr: string = e.target.result;
      const wb: XLSX.WorkBook = XLSX.read(bstr, { type: 'binary' });
      callback(wb);
    };
    reader.readAsBinaryString(target.files[0]);
  }

  private _render(): Blob {
    let file = XLSX.write(this.wb, { type: this.type, bookType: this.bookType });
    return new Blob([this._s2ab(file)]);
  }

  private _s2ab(s) {
    let buf = new ArrayBuffer(s.length);
    let view = new Uint8Array(buf);
    for (var i = 0; i != s.length; ++i) {
      view[i] = s.charCodeAt(i) & 0xFF;
    }
    return buf;
  }

  private _validSaveAs() {
    if (saveAs == null || saveAs == undefined) {
      throw new Error('saveAs is undefined, please import it.');
    }
  }

}
